<template>
	<div class="relative" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
		<div ref="referenceTarget">
			<!-- 具名插槽：触发弹层的视图 -->
			<slot name="reference"></slot>
		</div>

		<!-- 气泡展示 -->
		<transition name="slide">
			<div
				ref="contentTarget"
				v-show="isVisible"
				class="absolute p-1 z-20 bg-white dark:bg-zinc-900 border dark:border-zinc-700 rounded-md"
				:style="contentStyle"
			>
				<!-- 匿名插槽：弹出层视图中展示的内容 -->
				<slot></slot>
			</div>
		</transition>
	</div>
</template>

<script>
const PROP_TOP_LEFT = 'top-left'
const PROP_TOP_RIGHT = 'top-right'
const PROP_BOTTOM_LEFT = 'bottom-left'
const PROP_BOTTOM_RIGHT = 'bottom-right'

// 定义指定位置的 Enum
const placementEnum = [
	PROP_TOP_LEFT,
	PROP_TOP_RIGHT,
	PROP_BOTTOM_LEFT,
	PROP_BOTTOM_RIGHT,
]
</script>

<script setup>
import { nextTick, ref, watch } from 'vue'

const props = defineProps({
	placement: {
		type: String,
		default: PROP_BOTTOM_LEFT,
		validator(val) {
			const result = placementEnum.includes(val)

			if (!result) {
				throw new Error(`placement必须是${placementEnum.join('、')}中的一个`)
			}

			return result
		},
	},
})

const onMouseenter = () => {
	isVisible.value = true
}

const onMouseleave = () => {
	isVisible.value = false
}

const isVisible = ref(false)

// 计算元素尺寸
const referenceTarget = ref(null)
const contentTarget = ref(null)
const useElementSize = (target) => {
	if (!target) return {}
	return {
		width: target.offsetWidth,
		height: target.offsetHeight,
	}
}

// 气泡样式
const contentStyle = ref({
	top: 0,
	left: 0,
})

watch(isVisible, (val) => {
	if (!val) return

	nextTick(() => {
		switch (props.placement) {
			// 左上
			case PROP_TOP_LEFT:
				contentStyle.value.top = 0
				contentStyle.value.left =
					-useElementSize(contentTarget.value).width + 'px'
				break
			// 右上
			case PROP_TOP_RIGHT:
				contentStyle.value.top = 0
				contentStyle.value.left =
					useElementSize(referenceTarget.value).width + 'px'
				break
			// 左下
			case PROP_BOTTOM_LEFT:
				contentStyle.value.top =
					useElementSize(referenceTarget.value).height + 'px'
				contentStyle.value.left =
					-useElementSize(contentTarget.value).width + 'px'
				break
			// 右下
			case PROP_BOTTOM_RIGHT:
				contentStyle.value.top =
					useElementSize(referenceTarget.value).height + 'px'
				contentStyle.value.left =
					useElementSize(referenceTarget.value).width + 'px'
				break
		}
	})
})
</script>

<style lang="scss" scoped>
.slide-enter-active {
	transition: opacity 0.3s, transform 0.3s;
}

.slide-leave-active {
	transition: opacity 0.3s, transform 0.3s;
}

.slide-enter-from,
.slide-leave-to {
	transform: translateY(20px);
	opacity: 0;
}
</style>
